<?php
/*
 * @Description: 喵付通支付类
 * @Author: QianLong
 * @Date: 2019-09-27 16:34:02
 * @LastEditors    : QianLong
 * @LastEditTime   : 2022-05-28 16:34:48
 */

namespace app\common\service;

class Ecmpay
{
    const secret_4681156491864621 = '632d9b0d7556ce296f33035c6b3dcbe8dfa53e65b47f97a8e7d9ccf8ccdf14df';
    /**
     * PC支付
     * @param array $orderData
     * @param string $notify_url
     * @param string $return_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-04-02 16:52:58
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-04-02 16:52:58
     * @doc http://doc.21ds.cn/index?project_id=4683630058798691&doc_id=4685709434024713
     */
    public static function alipayForPc(array $orderData, string $return_url, string $notify_url = null)
    {
        if (!isset($orderData['out_trade_no']) || empty($orderData['out_trade_no'])) {
            throw new \Exception("缺少商户单号");
        }
        if (!isset($orderData['total_amount']) || empty($orderData['total_amount'])) {
            throw new \Exception("缺少金额");
        }
        if (!isset($orderData['subject']) || empty($orderData['subject'])) {
            throw new \Exception("缺少订单标题");
        }
        $apiUrl = 'https://openapi.ecmpay.cn/alipay/pcPay';
        $postData['app_id'] = '4681156491864621'; //应用APPID
        $postData['auth_app_id'] = '2016031701220293'; //授权的支付宝应用APPID
        $postData['trade_no'] = $orderData['out_trade_no'];
        $postData['total_amount'] = $orderData['total_amount'];
        $postData['subject'] = $orderData['subject'];
        $postData['notify_url'] = $notify_url;
        $postData['return_url'] = $return_url;
        $postData['sign'] = self::createSign($postData);
        $result = self::httpRequest($apiUrl, $postData);
        $result2 = json_decode($result['data'], true);
        if (!empty($result2) && isset($result2['code'])) {
            throw new \Exception($result2['msg']);
        } else {
            return $result['data'];
        }
    }
    /**
     * App支付
     * @param array $orderData
     * @param string $return_url
     * @param string $notify_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 12:07:27
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 12:07:27
     */
    public function alipayForApp(array $orderData, string $return_url, string $notify_url = null)
    {
        if (!isset($orderData['out_trade_no']) || empty($orderData['out_trade_no'])) {
            throw new \Exception("缺少商户单号");
        }
        if (!isset($orderData['total_amount']) || empty($orderData['total_amount'])) {
            throw new \Exception("缺少金额");
        }
        if (!isset($orderData['subject']) || empty($orderData['subject'])) {
            throw new \Exception("缺少订单标题");
        }
        $apiUrl = 'https://openapi.ecmpay.cn/alipay/appPay';
        $postData['app_id'] = '4681156491864621'; //应用APPID
        $postData['auth_app_id'] = '2016031701220293'; //授权的支付宝应用APPID
        $postData['trade_no'] = $orderData['out_trade_no'];
        $postData['total_amount'] = $orderData['total_amount'];
        $postData['subject'] = $orderData['subject'];
        $postData['notify_url'] = $notify_url;
        // $postData['return_url'] = $return_url;
        $postData['sign'] = self::createSign($postData);
        $result = self::httpRequest($apiUrl, $postData);
        $result2 = json_decode($result['data'], true);
        if (!empty($result2) && isset($result2['code'])) {
            throw new \Exception($result2['msg']);
        } else {
            return $result['data'];
        }
    }
    /**
     * 商家扫码支付
     * @param array $orderData
     * @param string $return_url
     * @param string $notify_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 17:48:24
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 17:48:24
     */
    public function scanPayQrcodePay(array $orderData, string $notify_url = null)
    {
        if (!isset($orderData['out_trade_no']) || empty($orderData['out_trade_no'])) {
            throw new \Exception("缺少商户单号：out_trade_no");
        }
        if (!isset($orderData['total_amount']) || empty($orderData['total_amount'])) {
            throw new \Exception("缺少金额：total_amount");
        }
        if (!isset($orderData['subject']) || empty($orderData['subject'])) {
            throw new \Exception("缺少订单标题：subject");
        }
        if (!isset($orderData['auth_code']) || empty($orderData['auth_code'])) {
            throw new \Exception("缺少支付授权码：auth_code");
        }
        try {
            if (!empty($notify_url)) {
                $result = Factory::payment()->FaceToFace()->asyncNotify($notify_url)->pay($orderData['subject'], $orderData['out_trade_no'], $orderData['total_amount'], $orderData['auth_code']);
            } else {
                $result = Factory::payment()->FaceToFace()->pay($orderData['subject'], $orderData['out_trade_no'], $orderData['total_amount'], $orderData['auth_code']);
            }
            $responseChecker = new ResponseChecker();
            //3. 处理响应或异常
            if ($responseChecker->success($result)) {
                return $result->body;
            } else {
                throw new \Exception('调用失败，原因：' . $result->msg . ',' . $result->subMsg);
            }
        } catch (\Exception $e) {
            throw new \Exception('调用失败，' . $e->getMessage());
        }
    }
    /**
     * 用户扫码支付
     * @param array $orderData
     * @param string $return_url
     * @param string $notify_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 17:48:24
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 17:48:24
     * @doc:https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate
     */
    public function createPayQrcodePay(array $orderData, string $notify_url = null)
    {
        if (!isset($orderData['out_trade_no']) || empty($orderData['out_trade_no'])) {
            throw new \Exception("缺少商户单号：out_trade_no");
        }
        if (!isset($orderData['total_amount']) || empty($orderData['total_amount'])) {
            throw new \Exception("缺少金额：total_amount");
        }
        if (!isset($orderData['subject']) || empty($orderData['subject'])) {
            throw new \Exception("缺少订单标题：subject");
        }
        $options = [];
        if (isset($orderData['seller_id']) && !empty($orderData['seller_id'])) {
            $options['seller_id'] = $orderData['seller_id'];
        }
        if (isset($orderData['body']) && !empty($orderData['body'])) {
            $options['body'] = $orderData['body'];
        }
        if (isset($orderData['timeout_express']) && !empty($orderData['timeout_express'])) {
            $options['timeout_express'] = $orderData['timeout_express'];
        }
        if (isset($orderData['merchant_order_no']) && !empty($orderData['merchant_order_no'])) {
            $options['merchant_order_no'] = $orderData['merchant_order_no'];
        }
        // 公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。支付宝只会在同步返回（包括跳转回商户网站）和异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝。
        if (isset($orderData['passback_params']) && !empty($orderData['passback_params'])) {
            $options['passback_params'] = $orderData['passback_params'];
        }
        try {
            if (!empty($notify_url)) {
                $result = Factory::payment()->FaceToFace()->asyncNotify($notify_url)->batchOptional($options)->precreate($orderData['subject'], $orderData['out_trade_no'], $orderData['total_amount']);
            } else {
                $result = Factory::payment()->FaceToFace()->batchOptional($options)->precreate($orderData['subject'], $orderData['out_trade_no'], $orderData['total_amount']);
            }
            $responseChecker = new ResponseChecker();
            //3. 处理响应或异常
            if ($responseChecker->success($result)) {
                return $result->body;
            } else {
                throw new \Exception('调用失败，原因：' . $result->msg . ',' . $result->subMsg);
            }
        } catch (\Exception $e) {
            throw new \Exception('调用失败，' . $e->getMessage());
        }
    }
    /**
     * 退款
     * @param array $orderData
     * @param string $return_url
     * @param string $notify_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 17:25:05
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 17:25:05
     */
    public function refund(array $orderData)
    {
        if (!isset($orderData['trade_no']) || empty($orderData['trade_no'])) {
            throw new \Exception("缺少支付宝交易号：trade_no");
        }
        if (!isset($orderData['refund_amount']) || empty($orderData['refund_amount'])) {
            throw new \Exception("缺少需要退款的金额：refund_amount");
        }
        $options = [];
        if (isset($orderData['reason']) && !empty($orderData['reason'])) {
            $options['refund_reason'] = $orderData['reason'];
        }
        if (isset($orderData['currency']) && !empty($orderData['currency'])) {
            $options['refund_currency'] = $orderData['currency'];
        }
        if (isset($orderData['out_request_no']) && !empty($orderData['out_request_no'])) {
            $options['out_request_no'] = $orderData['out_request_no'];
        }
        try {
            $result = Factory::payment()->common()->batchOptional($options)->refund($orderData['trade_no'], $orderData['refund_amount']);
            $responseChecker = new ResponseChecker();
            //3. 处理响应或异常
            if ($responseChecker->success($result)) {
                return $result->body;
            } else {
                throw new \Exception('调用失败，原因：' . $result->msg . ',' . $result->subMsg);
            }
        } catch (\Exception $e) {
            throw new \Exception('调用失败，' . $e->getMessage());
        }
    }
    /**
     * 转帐到支付宝
     * @param array $orderData
     * @param string $remark
     * @param boolean $isRedPacket
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 17:02:19
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 17:02:19
     */
    public function transfer(array $orderData, $remark = '', $isRedPacket)
    {
        if (!isset($orderData['out_trade_no']) || empty($orderData['out_trade_no'])) {
            throw new \Exception("缺少商户单号");
        }
        if (!isset($orderData['total_amount']) || empty($orderData['total_amount'])) {
            throw new \Exception("缺少金额");
        }
        if (!isset($orderData['subject']) || empty($orderData['subject'])) {
            throw new \Exception("缺少标题");
        }
        if (!isset($orderData['alipay']) || empty($orderData['alipay'])) {
            throw new \Exception("缺少支付宝账号：alipay");
        }
        if (!isset($orderData['name']) || empty($orderData['name'])) {
            throw new \Exception("缺少支付宝账户姓名：name");
        }
        try {
            $bizParams = [
                "order_title" => $orderData['subject'],
                "out_biz_no" => $orderData['out_trade_no'],
                "trans_amount" => $orderData['total_amount'],
                "payee_info" => ["identity" => $orderData['alipay'], "identity_type" => 'ALIPAY_LOGON_ID', "name" => $orderData['name']],
                "remark" => $remark,
                "product_code" => ($isRedPacket) ? "STD_RED_PACKET" : "TRANS_ACCOUNT_NO_PWD",
                "biz_scene" => "DIRECT_TRANSFER"
            ];
            $result = Factory::util()->generic()->execute('alipay.fund.trans.uni.transfer', null, $bizParams);
            if ($result->code == 10000 || $result->code == '10000') {
                $array = json_decode($result->httpBody, true)['alipay_fund_trans_uni_transfer_response'];
                // $array['order_id'] 支付宝转账订单号
                // $array['out_biz_no'] 商户订单号
                // (可选) $array['pay_fund_order_id'] 支付宝支付资金流水号
                // $array['trans_date'] 订单支付时间，格式为yyyy-MM-dd HH:mm:ss
                // (可选) $array['status'] 转账单据状态。SUCCESS：成功；FAIL：失败（具体失败原因请参见error_code以及fail_reason返回值）；DEALING：处理中；REFUND：退票；
                return $array;
            }
            throw new \Exception('打款失败，原因：' . $result->msg . ',' . $result->subMsg);
        } catch (\Exception $e) {
            throw new \Exception('调用失败，' . $e->getMessage());
        }
    }
    /**
     * 网络请求
     * @param string $url
     * @param array $data
     * @param integer $timeout
     * @param boolean $https
     * @param array $header
     * @return array
     * @author QianLong <87498106@qq.com>
     * @version 1.0.0
     * @date 2019-09-21 10:07:47
     */
    public static function httpRequest($url, $data = '', $timeout = 15, $https = false, $header = [])
    {
        $results['error']   = '';
        $results['status']  = 0;
        $results['data']    = array();
        $curl               = curl_init();                              // 启动一个CURL会话
        if (is_array($data) || is_array(json_decode($data, true))) {
            curl_setopt($curl, CURLOPT_POST, 1);
            if (!empty($data)) {
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            }
        }
        if (!empty($header) && is_array($header)) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);              // Post提交的数据包
        }
        if ($https) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);              // 对认证证书来源的检查
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);              // 从证书中检查SSL加密算法是否存在
            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);              // 使用自动跳转
        }
        curl_setopt($curl, CURLOPT_URL, $url);                      // 要访问的地址
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);                    // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_HEADER, 0);                      // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);              // 获取的信息以文件流的形式返回
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1);                 // 自动设置Referer

        $results['data'] = curl_exec($curl);                     // 执行操作
        if (curl_errno($curl)) {
            $results['error']    = curl_error($curl);                    //捕抓异常
        }
        curl_close($curl);                                              // 关闭CURL会话
        return $results;
    }
    /**
     * 生成签名
     * @param [type] $param
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2022-03-11 17:20:48
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2022-03-11 17:20:48
     */
    public static function createSign($param)
    {
        $secret = self::secret_4681156491864621;
        if (is_array($param)) {
            unset($param['sign']);
            foreach ($param as $k => $v) {
                if (!empty($v)) {
                    $Parameters[$k] = $v;
                }
            }
            unset($param, $k, $v);
            // 签名步骤一：按字典序排序参数
            ksort($Parameters);
            $buff = '';
            foreach ($Parameters as $k => $v) {
                $buff .= $k . $v;
            }
            unset($Parameters, $k, $v);
            $buff = $secret . $buff . $secret;
            $sign = strtoupper(md5($buff));
            unset($buff, $appInfo);
            return $sign;
        }
    }
}
